
#include "EggNinePCH.h"

#include "waveTechnique.h"

namespace EggNine {


WaveSolveTechnique::WaveSolveTechnique(GLsizei maxWidth, GLsizei maxHeight)
    :   IProgram("wave", "WaveSolve")
    ,   m_maxWidth(maxWidth)
    ,   m_maxHeight(maxHeight)
{
    glGenFramebuffers(1, &m_fbo);
    glGenTextures(1, &m_tmpTex);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_tmpTex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, maxWidth, maxHeight, 0, GL_RED, GL_FLOAT, NULL);

    glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_tmpTex, 0);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    char log[5000];
    int effect;
    effect=glfxGenEffect();
    glfxParseEffectFromFile(effect, GRAPHICS_PATH("shaders/waveSim.glsl"));
    m_GaussianProgramId=glfxCompileProgram(effect, "GaussianFilter");
    glfxGetEffectLog(effect, log, sizeof(log)-1);
    fprintf(stderr, log);

    m_programId=m_waveProgramId=glfxCompileProgram(effect, "WaveSim");
    glfxGetEffectLog(effect, log, sizeof(log)-1);
    fprintf(stderr, log);

    glProgramUniform2f(m_waveProgramId, glGetUniformLocation(m_waveProgramId, "SizeRatio"), 1.f, 1.f);

    m_attribMap["Position"]=glGetAttribLocation(m_waveProgramId, "Position");

    TexUnit tUnit;
    tUnit.m_location=glGetUniformLocation(m_waveProgramId, "u_t");
    tUnit.m_unit=0;
    tUnit.m_target=GL_TEXTURE_2D;
    tUnit.m_sampler=glfxGenerateSampler(effect, "WaveSampler");
    m_texUnits["u_t"]=tUnit;
    tUnit.m_location=glGetUniformLocation(m_waveProgramId, "u_pt");
    tUnit.m_unit=1;
    m_texUnits["u_pt"]=tUnit;
    tUnit.m_location=glGetUniformLocation(m_waveProgramId, "impulse");
    tUnit.m_unit=2;
    m_texUnits["impulse"]=tUnit;
    tUnit.m_location=glGetUniformLocation(m_waveProgramId, "u_stepConst");
    tUnit.m_unit=3;
    m_texUnits["u_stepConst"]=tUnit;

    glfxDeleteEffect(effect);

    m_uniformDir=glGetUniformLocation(m_GaussianProgramId, "Direction");
    m_uniformSizeRatio=glGetUniformLocation(m_GaussianProgramId, "SizeRatio");

    glGenVertexArrays(1, &m_vao);
    glGenBuffers(1, &m_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, m_vbo);

    //  2---3
    //  |\  |
    //  | \ |
    //  |  \|
    //  0---1
    const GLfloat loc[] = {
        0.f, 0.f,
        1.f, 0.f,
        0.f, 1.f,
        1.f, 1.f
    };
    glBufferData(GL_ARRAY_BUFFER, sizeof(loc), loc, GL_STATIC_DRAW);

    glBindVertexArray(m_vao);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

WaveSolveTechnique::~WaveSolveTechnique()
{
    glDeleteFramebuffers(1, &m_fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glDeleteTextures(1, &m_tmpTex);
    glDeleteBuffers(1, &m_vbo);
    glDeleteVertexArrays(1, &m_vao);
    glDeleteProgram(m_waveProgramId);
    glDeleteProgram(m_GaussianProgramId);
}

void WaveSolveTechnique::MapTextures() const
{
    IProgram::MapTextures();
    glProgramUniform1i(m_GaussianProgramId, glGetUniformLocation(m_GaussianProgramId, "u_t"), 0);
    glProgramUniform1i(m_GaussianProgramId, glGetUniformLocation(m_GaussianProgramId, "u_stepConst"), m_texUnits.at("u_stepConst").m_unit);
}

void WaveSolveTechnique::ResetStateToProgram() const
{
    glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
    glViewport(0, 0, m_width, m_height);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_STENCIL_TEST);
}

void WaveSolveTechnique::SetTextureSize(GLsizei width, GLsizei height) const
{
    m_width=width;
    m_height=height;
}

void WaveSolveTechnique::DrawPass(int pass/* =0 */) const
{
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, m_targetTex, 0);

    glDrawBuffer(GL_COLOR_ATTACHMENT1);
    glUseProgram(m_waveProgramId);
    glBindVertexArray(m_vao);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glUseProgram(m_GaussianProgramId);

    glDrawBuffer(GL_COLOR_ATTACHMENT0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_targetTex);
    glUniform2f(m_uniformSizeRatio, 1.f, 1.f);
    glUniform2i(m_uniformDir, 1, 0);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glDrawBuffer(GL_COLOR_ATTACHMENT1);
    glBindTexture(GL_TEXTURE_2D, m_tmpTex);
    glUniform2i(m_uniformDir, 0, 1);
    glUniform2f(m_uniformSizeRatio, (GLfloat)m_width/m_maxWidth, (GLfloat)m_height/m_maxHeight);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    // Detach the target texture
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, 0, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

void WaveSolveTechnique::GetActiveUniforms(const UniformMap& uBlocks)
{
    m_uBlocks.push_back(uBlocks.at("WaveSimParams"));
}

} // EggNine